#include <iostream>
#include <fstream>
#include <iomanip>

#include <fcntl.h>

#include "IOError.h"
#include "Counter_DL636G1.h"
#include "DigitalIO_DL636F.h"
#include "ADC_DL642A.h"
#include "teebuf.h"

using namespace std;

unsigned int measuretime=10, cycletime=2000000;
char *rawfnb = "raw_";   // RAW data File Name Base
char *histfnb = "hist_"; // HISTogramm data File Name Base
char *logfnb = "log_";   // LOG File Name Base
char *logext = ".txt";   // LOG file name EXT
char *fnext = ".dat";    // Filen Name EXT

char *rawfn, *histfn, *logfn;   // filenames for raw data and history

char *devfn="/dev/sis1100_00remote";
char *stamp=NULL;

unsigned int adc_addr = 0xb200;

void build_fn();
void show_help(char *progname);
int cmdlineargs(int argc,char *argv[]);

/*---------------------------------------------------------------------------*/
int main(int argc, char *argv[])
/*---------------------------------------------------------------------------*/
{
  int crate;

  cmdlineargs(argc,argv);

  build_fn();

  // open files for writing
  ofstream raws(rawfn);
  ofstream hists(histfn);
  ofstream logs(logfn);
  if(!raws) { cerr << "error: could not open " << rawfn << endl; exit(1); }
  if(!hists) { cerr << "error: could not open " << histfn << endl; exit(1); }
  if(!logs) { cerr << "error: could not open " << logfn << endl; exit(1); }

  teebuf tee( cerr.rdbuf(), logs.rdbuf() );
  ostream log(&tee);

  log << "spektrum 0.0" << endl << endl;  

  log << "rawfn: " << rawfn << endl;
  log << "histfn: " << histfn << endl;
  log << "logfn: " << logfn << endl;
  log << "sis device: " << devfn << endl;

  log << "measuretime:" << measuretime << " cycletime: " << cycletime << endl;

    
  // open sis1100/3100 pci-to-vme link
  if( !(crate=open(devfn, O_RDWR, 0)) )
  {
    log << "ERROR: could not open \"" << devfn << "\"" << endl;
    exit(1);
  }
  
  vmesysreset(crate);

  // instantiate io object
  DigitalIO_DL636F io1(crate,0x9900);

  // instantiate adc object
  ADC_DL642A adc1(crate,adc_addr);
  log << "adc.addr=" << hex << adc1.getAddress() << dec << endl;

  // used adc on adc module (0..2)
  //  int fifono=1;
  
  Counter_DL636G1 counter1(crate,0x9940);

  try {

    // init counter1
    counter1.reset();

    //init io1
    io1.setO1(0); io1.setO2(0);

    // reset external counters via io1.O3
    io1.setO3(0); usleep(500);
    io1.setO3(1); usleep(500);
    io1.setO3(0);

    // init adc1
    log << "adc1: init" << endl;
    adc1.reset();
    adc1.clearFIFO();
    adc1.setEnabled(1);

    // display current state of adc1
    log << "adc:"
	 << " hitcounter = " << adc1.getHitCounter()
	 << " enable = " << adc1.isEnabled() 
         << " status = " << setw(4) << setfill('0') << adc1.getStatus()
         << " busy = " << io1.getInput() << endl;
   
    // enable measurement through O1 of io1

    // clear histogramm
    // adc has 3 x 2^14 = 3 x 16384 channels
    u_int16_t histogramm[3][16384];
    for(int i=0; i<16384; i++)
      for(int j=0; j<3; j++ ) { histogramm[j][i] = 0; }

    u_int32_t hitcounter;

    log << "measuring..." << endl;
    time_t start = time(0);
    io1.setO2(1); // enable counter1 via O2
    io1.setO1(1);

    unsigned int readcounter = 0, delta=0;
    double dt;
    while((dt=difftime(time(0),start)) < measuretime) {
      if(cycletime != 0) usleep(cycletime);

      //io1.setO1(0);
      // read out channels of adc1 and built histogramm
      hitcounter = adc1.getHitCounter();
      delta = hitcounter - readcounter;

      log << " dt= " << dt 
	   << " adc1.hitcounter=" << hitcounter
           << " readcounter=" << readcounter
	   << " delta=" << delta << endl;
      
      for(unsigned int i=0; i<delta; i++) {
	raws << readcounter;

	for(int j=0; j<3; j++ ) {
	  u_int16_t d = adc1.readFIFO32(j);
	  raws << " " << d;
	  histogramm[j][d]++;
	}
        readcounter++;

	raws << endl;
      }
      //io1.setO1(1);
    }
    io1.setO1(0);
    io1.setO2(0);
    log << endl << "finished measurement" << endl; 

    log << endl
	 << "counter1.A==" << counter1.getA() << endl
         << "counter1.B==" << counter1.getB() << endl;

    // display current state of adc1
    log << "adc:"
	 << " hitcounter = " << adc1.getHitCounter()
	 << " enable = " << adc1.isEnabled() 
         << " status = " << setw(4) << setfill('0') << adc1.getStatus()
         << " busy = " << io1.getInput() << endl;

    // read the rest from adc fifo
    hitcounter = adc1.getHitCounter();
    delta = hitcounter - readcounter;

    log << "readcounter = " << readcounter 
         << "  so a delta = " << delta << " words left to read out" << endl;

    for(unsigned int i=0; i<delta; i++) {
      raws << readcounter;
      
      for(unsigned int j=0; j<3; j++ ) {
	u_int16_t d = adc1.readFIFO32(j);
	raws << " " << d;
	histogramm[j][ d ]++; 
      }
      readcounter++;

      raws << endl;
    }

    // output histgramm data
    for(int i=0; i<16384; i++) {
      hists << i;
      for(int j=0; j<3; j++ )
	hists << " " << histogramm[j][i];
      hists << endl;
    }
       
  }
  catch( IOError ioe ) {
    log << "catched IOError exception: " << ioe.getMsg() << endl 
	 << "param=" << ioe.getParam() << endl << endl;

    log << "setting outputs of io1 to zero" << endl << endl;
    io1.setO1(0); io1.setO2(0); io1.setO3(0);

    log << "trying to read status of used components:" << endl << endl 
         << "adc1.hitcounter: " << adc1.getHitCounter() << endl
         << "     counter1.A: " << counter1.getA() << endl
         << "     counter2.B: " << counter1.getA() << endl;
  }

  close(crate);

  hists.close();
  raws.close();

  

  return 0;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void show_help(char *progname)
/*---------------------------------------------------------------------------*/
{
  cerr << "usage: " << progname << " [options]" << endl
       << endl
       << "options:" << endl
       << " -?         shows this help" << endl
       << " -r <base>  use <base> as base for raw-data file name" << endl
       << " -h <base>  use <base> as base for histogram file name" << endl
       << " -t <time>  set measure time to <time> seconds" << endl
       << " -c <time>  set cycle time to <time> microseconds" << endl
       << " -a <addr>  set address of adc to <addr>" << endl
       << " -d <path>  set sis1100 device file to <path>" << endl
       << " -s <stamp> use <stamp> instead of default time stamp (%y%m%d_%H%M)" << endl
       << endl;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void build_fn()
/*---------------------------------------------------------------------------*/
{
  if(!stamp){
    stamp = new char[1024];
    time_t dummy; time(&dummy);
    strftime(stamp, 1023, "%y%m%d_%H%M", localtime(&dummy) );
  }

  rawfn = new char[ strlen(rawfnb) + strlen(stamp) + strlen(fnext) + 1 ];
  strcpy(rawfn,rawfnb); strcat(rawfn,stamp); strcat(rawfn,fnext);
  histfn = new char[ strlen(histfnb) + strlen(stamp) + strlen(fnext) + 1 ];
  strcpy(histfn,histfnb); strcat(histfn,stamp); strcat(histfn,fnext);
  logfn = new char[ strlen(logfnb) + strlen(stamp) + strlen(logext) + 1 ];
  strcpy(logfn,logfnb); strcat(logfn,stamp);  strcat(logfn,logext);
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
int cmdlineargs(int argc,char *argv[])
/*---------------------------------------------------------------------------*/
{
    char c;

    opterr = 0;
     
    while( (c = getopt (argc, argv, "?r:h:t:c:d:a:s:") ) != -1)
	switch (c) {
	    case '?':
	      show_help(argv[0]); exit(0);
	      break;

	    case 'r':	      
	      if(!(rawfnb = new char[strlen(optarg)+1])) {
		cerr << "error: cmdlineargs(): could not allocate memory"
		     << endl;
		exit(1);
	      }
	      strcpy(rawfnb,optarg);
	      break;

	    case 'h':
	      if(!(histfnb = new char[strlen(optarg)+1])) {
		cerr << "error: cmdlineargs(): could not allocate memory"
		     << endl;
		exit(1);
	      }
	      strcpy(histfnb,optarg);
	      break;

	    case 'l':
	      if(!(logfnb = new char[strlen(optarg)+1])) {
		cerr << "error: cmdlineargs(): could not allocate memory"
		     << endl;
		exit(1);
	      }
	      strcpy(logfnb,optarg);
	      break;

	    case 't':
	      measuretime = atoi(optarg);
	      break;

	    case 'c':
	      cycletime = atoi(optarg);
	      break;

	    case 'a':
	      sscanf(optarg,"%x",&adc_addr);
	      break;

	    case 'd':
	      if(!(devfn = new char[strlen(optarg)+1])) {
		cerr << "error: cmdlineargs(): could not allocate memory"
		     << endl;
		exit(1);
	      }
	      strcpy(devfn,optarg);
	      break;

	    case 's':
	      if(!(stamp = new char[strlen(optarg)+1])) {
		cerr << "error: cmdlineargs(): could not allocate memory"
		     << endl;
		exit(1);
	      }
	      strcpy(stamp,optarg);
	      break;

	    default:
	      abort();
	}

    return 0;
}
/*---------------------------------------------------------------------------*/
